<?php
/**
 * Copyright (c) Mauricio Cuenca
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. The name of the author may not be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 * 
 */
require_once 'Exception.php';

class Chart {
	const API_URL = 'http://chart.apis.google.com/chart?';
	const CHART_TYPE_LABEL = 'cht';
	const PARAM_SEPARATOR = '&';

	/**
	 * Chart properties
	 */
	protected static $size        = null;
	protected static $data        = null;
	protected static $type        = null;
	protected static $labels      = null;
	protected static $axis        = null;
	protected static $axis_labels = null;

	/**
	 * This object can not be instantiaded
	 * 
	 */
	private function __construct() {}

	/**
	 * Sets the size of the chart
	 * 
	 * @param  integer $width
	 * @param  integer
	 * @return void
	 */
	public function setSize($width, $height) {
		define(MAX_WIDTH_HEIGHT, 1000);
		define(MAX_AREA, 300000);
		define(SIZE_ID, 'chs');

		// Cast to integer
		$width  = intval($width);
		$height = intval($height);

		// Checks appropriate width and height
		if ($width > MAX_WIDTH_HEIGHT || $height > MAX_WIDTH_HEIGHT) {
			throw new Chart_Exception('Width or height value exceeds allowed ' . MAX_WIDTH_HEIGHT . 
									  'pixels limit');
		} else if ($width * $height > MAX_AREA) {
			throw new Chart_Exception('Chart area exceeds allowed ' . MAX_AREA . 'pixels limit');
		}

		self::$size = SIZE_ID . '=' . $width . 'x' . $height;
	}

	/**
	 * Sets the chart data
	 * 
	 * @param  array $data
	 * @param  array $series
	 * @return void
	 */
	public function setData($data, $series = array()) {
		define(DATA_STRING, 'chd=t:');
		define(SERIES_STRING, '&chds=');

		if (!is_array($data)) {
			throw new Chart_Exception('Data must be an array');
		}

		self::$data = DATA_STRING . implode(',', $data);

		// Checks if is thera any data series
		if (!empty($series)) {
			self::$data .= SERIES_STRING . implode(',', $series);
		}
	}

	/**
	 * Shows the axis, by default the bottom X axis is shown
	 * 
	 * @param  bool $left
	 * @param  bool $top
	 * @param  bool $right
	 * @return void
	 */
	public function showAxis($left = false, $top = false, $right = false) {
		define(AXIS_ID,     'chxt=');
		define(BOTTOM_AXIS, 'x');
		define(LEFT_AXIS,   'y');
		define(TOP_AXIS,    't');
		define(RIGHT_AXIS,  'r');

		self::$axis = AXIS_ID . BOTTOM_AXIS;

		// If more axis to be shown
		self::$axis .= $left  ? ',' . LEFT_AXIS  : '';
		self::$axis .= $top   ? ',' . TOP_AXIS   : '';
		self::$axis .= $right ? ',' . RIGHT_AXIS : '';
	}

	/**
	 * Sets the labels for the shown axis. Labels should be passed as a single
	 * position array for each axis
	 * 
	 * @param  array $bottom
	 * @param  array $left
	 * @param  array $top
	 * @param  array $right
	 * @return void
	 */
	public function setAxisLabels($bottom, $left = array(), $top = array(), $right = array()) {
		define(AXIS_LABELS_ID, 'chxl=');

		if (empty(self::$axis)) {
			throw new Chart_Exception('Axis must be shown prior to setting their values');
		}

		self::$axis_labels = AXIS_LABELS_ID . '0:|' . implode('|', $bottom) . '|';

		// If more axis labels to be shown
		self::$axis_labels .= !empty($left)  ? '1:' . implode('|', $left) . '|' : '';
		self::$axis_labels .= !empty($top)   ? '2:' . implode('|', $top) . '|' : '';
		self::$axis_labels .= !empty($right) ? '3:' . implode('|', $right) . '|' : '';
	}

	/**
	 * Sets the chart labels
	 * 
	 * @param  array $data
	 * @return void
	 */
	public function setLabels($data) {
		define(LABELS_ID, 'chl');

		if (!is_array($data)) {
			throw new Chart_Exception('Labels must be an array');
		}

		self::$labels = LABELS_ID . '=' . implode('|', $data);
	}

	/**
	 * Gets the URL as a string
	 * 
	 * @return string
	 */
	public function getURL() {
		$url = self::API_URL;

		$url .= isset(self::$size) ? self::$size . self::PARAM_SEPARATOR : '';
		$url .= isset(self::$data) ? self::$data . self::PARAM_SEPARATOR : '';
		$url .= isset(self::$type) ? self::$type . self::PARAM_SEPARATOR : '';
		$url .= isset(self::$labels) ? self::$labels . self::PARAM_SEPARATOR : '';
		$url .= isset(self::$axis) ? self::$axis . self::PARAM_SEPARATOR : '';
		$url .= isset(self::$axis_labels) ? self::$axis_labels . self::PARAM_SEPARATOR : '';

		return $url;
	}

	/**
	 * Opens the chart URL
	 * 
	 * @return void
	 */
	public function openURL() {
		header('Location: ' . $this->getURL(), true);
	}
}